Skip to content

Conversation

@sukangpunch
Copy link
Contributor

관련 이슈

작업 내용

  1. 멘토 승격 지원서를 어드민이 승인/거절 하는 기능을 추가하였습니다.

  2. 멘토 승격 지원서들의 상태(PENDDING, APPROVED, REJECTED) 별 지원서 개수를 조회합니다.

특이 사항

멘토 승격 지원서의 COUNT를 가져올 때 JPQL 로 한번에 가져오는 방법과 JPA 메서드를 활용하여 상태 별로 따로 조회 하는 방법 을 생각 했었는데, jpa 메서드를 활용하여 상태 별로 따로 조회 하도록 하였습니다.

이유

  1. admin 패키지의 DTO를 mentor 패키지(Repository)에서 참조하면 역방향 의존성 발생
  • Object[] 로 받거나, mentor 패키지에서 dto 를 따로 만들어도 되지만 비 효율 적이라 판단
  1. JPA 메서드를 활용해서 쿼리 작성 없이 레포지토리 코드 유지 가능
  2. 멘토 지원서는 대량 데이터가 아니어서 당장 성능 차이가 크지 않음
  3. 어드민 기능이라 일반 사용자의 사용성에 영향을 주지 않음

추후 브루노 작성도 하겠습니다!

리뷰 요구사항 (선택)

@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

  1. REST 엔드포인트 확장: AdminMentorApplicationController에 멘토 신청 승인(POST /admin/mentor-applications/{id}/approve), 거절(POST /admin/mentor-applications/{id}/reject), 개수 조회(GET /admin/mentor-applications/count) 3개 엔드포인트가 추가되었습니다.
  2. DTO 추가: MentorApplicationCountResponse(approvedCount, pendingCount, rejectedCount)와 MentorApplicationRejectRequest(rejectedReason, @NotBlank) 레코드가 도입되었습니다.
  3. 서비스 로직 추가: AdminMentorApplicationService에 approveMentorApplication, rejectMentorApplication, getMentorApplicationCount 메소드가 추가되어 엔티티 로드·검증·상태 변경 및 상태별 카운트를 수행합니다.
  4. 도메인 상태 전환: MentorApplication에 approve()와 reject(String) 메소드가 추가되어 PENDING 전용 검증 후 상태를 APPROVED/REJECTED로 전환하고 승인 시각 또는 거절 사유를 설정합니다.
  5. 리포지토리 확장: MentorApplicationRepository에 상태별 카운트용 countByMentorApplicationStatus 메소드 시그니처가 추가되었습니다.
  6. 에러 코드 추가: ErrorCode에 MENTOR_APPLICATION_ALREADY_CONFIRMED와 MENTOR_APPLICATION_UNIVERSITY_NOT_SELECTED 상수가 추가되었습니다.
  7. 테스트 보강: AdminMentorApplicationServiceTest에 승인·거절 경로 및 상태별 카운트 검증을 위한 여러 테스트 케이스(중복된 블록 포함)가 추가되어 리포지토리 기반 상태 검증을 수행합니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주목할 파일:
    • src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java — 상태 전환 조건과 approvedAt 타임스탬프 설정 검토.
    • src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java — 트랜잭션 경계와 예외 매핑(존재하지 않음, 대학 미선택) 확인.
    • src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java — @Valid 적용 및 빈 바디 응답 처리 검토.
    • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java — 테스트 중복 여부와 리포지토리 상태 초기화/검증 적절성 검토.

Suggested reviewers

  • wibaek
  • Hexeong
  • lsy1307

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 추가된 기능(승인/거절 기능, 상태별 개수 조회)을 명확하게 요약하고 있으며 변경의 핵심을 잘 전달합니다.
Description check ✅ Passed PR 설명이 템플릿의 필수 섹션(관련 이슈, 작업 내용)을 포함하고 있으며, 기술적 결정사항도 상세히 설명되어 있습니다.
Linked Issues check ✅ Passed 코드 변경사항이 이슈 #575의 모든 요구사항을 충족합니다: 승인/거절 엔드포인트, 상태별 개수 조회 기능, 필요한 도메인 로직과 DTO가 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #575의 요구사항(승인/거절 기능, 상태별 개수 조회)과 직접 관련이 있으며, 범위를 벗어난 변경이 없습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78fc492 and c45a67c.

📒 Files selected for processing (2)
  • src/main/java/com/example/solidconnection/admin/dto/MentorApplicationCountResponse.java (1 hunks)
  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/example/solidconnection/admin/dto/MentorApplicationCountResponse.java
  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1)

132-132: 1) MENTOR_APPLICATION_ALREADY_CONFIRM 네이밍·메시지를 기존 패턴에 더 맞게 다듬는 것을 고려해 주세요.
기존 상수 MENTORING_ALREADY_CONFIRMED와 맞추려면 이름을 MENTOR_APPLICATION_ALREADY_CONFIRMED 정도로 정리하면 읽을 때 통일감이 더 좋아질 것 같습니다.
또한 사용자 메시지의 “요청 입니다.”는 “요청입니다.”로 붙여 쓰는 편이 자연스러우니, 아래처럼 문구만 먼저 정리해 두셔도 좋겠습니다.

-    MENTOR_APPLICATION_ALREADY_CONFIRM(HttpStatus.BAD_REQUEST.value(), "이미 승인 또는 거절된 멘토 승격 요청 입니다."),
+    MENTOR_APPLICATION_ALREADY_CONFIRM(HttpStatus.BAD_REQUEST.value(), "이미 승인 또는 거절된 멘토 승격 요청입니다."),
src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java (1)

3-6: 1) 멘토 승격 승인/거절 도메인 메서드가 명확하지만, 핵심 규칙을 조금 더 강하게 표현할 여지가 있습니다.
PENDING 상태에서만 approve/reject를 허용하고, 이미 처리된 경우 MENTOR_APPLICATION_ALREADY_CONFIRM 예외를 던지는 흐름은 도메인 규칙을 잘 드러내고 있습니다.
다만 비즈니스 규칙상 APPROVED 상태의 MentorApplication은 universityId가 항상 채워져야 하므로, approve() 내부에서 universityId가 null이면 예외를 던지도록 한 번 더 방어해 두면 상태 일관성을 도메인 레벨에서 확실히 보장할 수 있습니다.
또한 reject(String rejectedReason)는 현재 DTO 검증에 의존하고 있지만, 향후 다른 호출 경로가 생길 가능성을 감안하면 rejectedReason가 null 또는 공백일 때 예외를 던지는 가벼운 방어 코드를 추가하는 것도 고려해 볼 만합니다.
Based on learnings, ...

Also applies to: 124-138

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d052325 and c6f51fb.

📒 Files selected for processing (8)
  • src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java (3 hunks)
  • src/main/java/com/example/solidconnection/admin/dto/MentorApplicationCountResponse.java (1 hunks)
  • src/main/java/com/example/solidconnection/admin/dto/MentorApplicationRejectRequest.java (1 hunks)
  • src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java (2 hunks)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java (2 hunks)
  • src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java (1 hunks)
  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: whqtker
Repo: solid-connection/solid-connect-server PR: 362
File: src/main/java/com/example/solidconnection/mentor/service/MentoringQueryService.java:0-0
Timestamp: 2025-07-04T10:41:32.999Z
Learning: 멘토링 관련 기능에는 페이지네이션을 적용하지 않는 것이 해당 프로젝트의 설계 방침이다.
📚 Learning: 2025-11-17T06:30:49.502Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.

Applied to files:

  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
  • src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java
  • src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java
  • src/main/java/com/example/solidconnection/admin/dto/MentorApplicationRejectRequest.java
  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
📚 Learning: 2025-11-20T14:03:56.450Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.

Applied to files:

  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
  • src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java
  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (12)
src/main/java/com/example/solidconnection/admin/dto/MentorApplicationCountResponse.java (1)

3-7: 1) 멘토 지원서 상태별 카운트 DTO 정의가 단순·명확합니다.
레코드 필드 이름과 타입이 서비스/컨트롤러에서 사용하는 의미와 잘 맞습니다.
현재 요구사항 기준으로는 추가 생성자나 정적 팩토리 없이도 충분해 보여 그대로 유지하셔도 좋겠습니다.

src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java (1)

16-16: 1) 상태별 카운트용 JPA 메서드 시그니처가 적절합니다.
파생 쿼리 메서드 이름이 mentorApplicationStatus 필드와 일관되고, long 반환 타입도 카운트 용도로 자연스럽습니다.
JPQL 없이도 요구사항을 충족하므로 레포지토리 복잡도를 늘리지 않는 좋은 선택으로 보입니다.

src/main/java/com/example/solidconnection/admin/dto/MentorApplicationRejectRequest.java (1)

6-10: 1) 거절 사유 요청 DTO의 검증 범위가 적절합니다.
@notblank@SiZe(max = 200) 조합으로 필수 입력·길이 제한이 명확하게 정의되어 있습니다.
DB 기본 컬럼 길이(보통 255)보다 작은 최대 길이를 사용하므로 저장 시 제약과도 안전하게 호환될 것으로 보입니다.

src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (4)

50-51: 테스트 검증을 위한 repository 의존성 추가가 적절합니다.

승인/거절 후 실제 상태 변경을 확인하기 위해 repository를 직접 조회하는 것은 통합 테스트에서 올바른 접근입니다.


223-272: 승인 기능에 대한 포괄적인 테스트 커버리지입니다.

다음 시나리오들을 체계적으로 검증하고 있습니다:

  1. 대기중인 지원서의 정상 승인 처리
  2. 이미 승인된 지원서에 대한 중복 승인 방어
  3. 이미 거절된 지원서에 대한 승인 시도 방어
  4. 존재하지 않는 지원서 처리

특히 Line 235-237에서 상태 변경과 approvedAt 타임스탬프를 모두 검증하는 것이 좋습니다.


274-327: 거절 기능에 대한 체계적인 테스트입니다.

승인 테스트와 일관된 패턴으로 다음을 검증합니다:

  1. 대기중인 지원서의 정상 거절 처리
  2. 이미 처리된 지원서들에 대한 방어 로직
  3. 존재하지 않는 지원서 처리

Line 287-289에서 거절 사유(rejectedReason)를 함께 검증하는 것이 적절합니다.


329-361: 상태별 카운트 기능에 대한 명확한 테스트입니다.

두 가지 시나리오를 검증합니다:

  1. 각 상태(APPROVED, PENDING, REJECTED)별 지원서 개수 집계
  2. 지원서가 없는 경우 모든 카운트가 0 반환

테스트가 직관적이고 검증 로직이 명확합니다.

src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java (2)

42-48: 거절 기능의 구현이 명확하고 적절합니다.

지원서를 조회하고 도메인 메서드에 거절 사유를 전달하는 로직이 간결합니다. 트랜잭션 처리와 에러 핸들링도 적절합니다.


50-61: 상태별 카운트 조회 방식이 합리적입니다.

각 상태별로 개별 쿼리를 실행하는 방식을 선택하신 것에 대해:

  • 장점: admin 패키지 DTO를 mentor 패키지 Repository에서 참조하지 않아 역방향 의존성 방지
  • 장점: JPA 메서드 명명 규칙으로 유지보수성 향상
  • 단점: 3번의 DB 쿼리 실행

멘토 지원서가 대량 데이터가 아니고 어드민 전용 기능이므로, 성능보다 의존성 구조를 우선한 판단이 적절해 보입니다.

src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java (3)

43-49: 승인 엔드포인트가 적절하게 구현되었습니다.

POST 메서드를 사용한 상태 변경 API 설계가 RESTful 원칙에 부합합니다.

한 가지 확인사항: 현재 코드에 어드민 권한 검증 로직이 보이지 않습니다. /admin/ 경로에 대한 권한 검증이 Spring Security 설정이나 인터셉터에서 처리되고 있는지 확인해주세요.


51-58: 거절 엔드포인트의 구현이 적절합니다.

다음 설계가 좋습니다:

  1. PathVariable로 대상 지원서 식별
  2. RequestBody로 거절 사유 전달
  3. @Valid 어노테이션으로 입력값 검증

RESTful 설계 원칙에 부합하고 명확한 API입니다.


60-64: 카운트 조회 엔드포인트가 명확하게 구현되었습니다.

GET 메서드를 사용한 조회 API로, 상태별 지원서 개수를 담은 DTO를 반환합니다. 설계가 직관적이고 적절합니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1)

132-133: 1. 에러 코드 네이밍 일관성 및 오타 수정이 필요합니다.

두 가지 개선 사항이 있습니다:

  1. 네이밍 일관성: Line 125의 MENTORING_ALREADY_CONFIRMED는 과거형 "CONFIRMED"를 사용하는데, 새로 추가된 코드는 "CONFIRM"을 사용하고 있습니다. 일관성을 위해 MENTOR_APPLICATION_ALREADY_CONFIRMED로 수정하는 것이 좋습니다.

  2. 메시지 오타: "멘토 승격 요청 입니다" → "멘토 승격 요청입니다" (불필요한 공백 제거)

-    MENTOR_APPLICATION_ALREADY_CONFIRM(HttpStatus.BAD_REQUEST.value(), "이미 승인 또는 거절된 멘토 승격 요청 입니다."),
+    MENTOR_APPLICATION_ALREADY_CONFIRMED(HttpStatus.BAD_REQUEST.value(), "이미 승인 또는 거절된 멘토 승격 요청입니다."),
     MENTOR_APPLICATION_UNIVERSITY_NOT_SELECTED(HttpStatus.BAD_REQUEST.value(), "승인하려는 멘토 신청에 대학교가 선택되지 않았습니다."),
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (1)

224-285: 1. 멘토 승격 지원서 승인 테스트가 잘 구성되어 있습니다.

테스트 케이스들이 비즈니스 규칙을 잘 반영하고 있습니다:

  • 대기중 → 승인 정상 케이스
  • 대학 미선택 시 예외 (learnings의 비즈니스 규칙과 일치)
  • 이미 승인/거절된 경우 예외
  • 존재하지 않는 지원서 예외

한 가지 작은 제안: Line 236, 300에서 Optional.get() 대신 orElseThrow()를 사용하면 테스트 실패 시 더 명확한 에러 메시지를 얻을 수 있습니다.

             // then
-            MentorApplication result = mentorApplicationRepository.findById(mentorApplication2.getId()).get();
+            MentorApplication result = mentorApplicationRepository.findById(mentorApplication2.getId())
+                    .orElseThrow(() -> new AssertionError("MentorApplication should exist"));
             assertThat(result.getMentorApplicationStatus()).isEqualTo(MentorApplicationStatus.APPROVED);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6f51fb and b7bf005.

📒 Files selected for processing (3)
  • src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java (2 hunks)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1 hunks)
  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.
📚 Learning: 2025-11-17T06:30:49.502Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.

Applied to files:

  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
📚 Learning: 2025-11-20T14:03:56.450Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.

Applied to files:

  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (2)

287-340: 2. 멘토 승격 지원서 거절 테스트가 체계적으로 작성되었습니다.

거절 시나리오에 대한 테스트 커버리지가 충분합니다:

  • 정상 거절 및 거절 사유 저장 검증
  • 이미 처리된 지원서 재거절 방지
  • 존재하지 않는 지원서 처리

rejectedReason 필드 검증까지 포함되어 있어 데이터 무결성 확인이 잘 되어 있습니다.


342-374: Based on the verification performed, the review comment identified a legitimate architectural concern about test isolation. The verification revealed that:

  1. Custom JUnit 5 extensions can implement AfterEachCallback to reset database state, which is the pattern used in this codebase via the custom DatabaseClearExtension.

  2. To ensure tests are repeatable and isolated, it is a good practice to ensure tests always start with a clean state, and in integration testing this often means bringing the database to a well-known state.

  3. The annotation does NOT include @Transactional, instead relying on the custom DatabaseClearExtension extension for isolation.

Given that @transactional is invaluable as it automatically rolls back database changes after each test, ensuring data isolation, and the project opts for a custom extension approach instead, the test isolation mechanism depends entirely on the DatabaseClearExtension implementation.

테스트 격리 메커니즘 확인 필요

  1. DatabaseClearExtensionAfterEachCallback을 구현하여 각 테스트 후 적절히 정리하는지 확인이 필요합니다. @Transactional을 사용하지 않으므로, 커스텀 확장이 정확하게 데이터베이스를 정리해야만 테스트 격리가 보장됩니다.

  2. 364번 줄의 deleteAll() 호출은 @BeforeEach로 설정되어 있어 테스트 실행 순서에 관계없이 각 테스트가 깨끗한 상태에서 시작하므로, 테스트 격리는 DatabaseClearExtension의 구현에 따라 결정됩니다.

  3. 현재 설정에서는 @Nested 클래스의 @BeforeEach 초기화 및 DatabaseClearExtension이 함께 작동하면 격리가 유지될 것으로 보이지만, 확장의 실제 동작을 확인하지 않았으므로 수동 검증을 권장합니다.

Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다 ~ 잘못된 점은 없는 거 같습니다. 개인적 의견만 몇 개 달았습니다 👍


public record MentorApplicationRejectRequest(
@NotBlank(message = "거절 사유는 필수입니다")
@Size(max = 200, message = "거절 사유는 200자를 초과할 수 없습니다")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

200자라는 조건은 회의를 통해 결정된 건가요 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

디자인 보고 감으로 작성해 놨었습니다....
명확한 기획 내용 기반으로 작성 한 건 아니여서 빼는게 좋겠네요!

public MentorApplicationCountResponse getMentorApplicationCount() {
long approvedCount = mentorApplicationRepository.countByMentorApplicationStatus(MentorApplicationStatus.APPROVED);
long pendingCount = mentorApplicationRepository.countByMentorApplicationStatus(MentorApplicationStatus.PENDING);
long rejectedCount = mentorApplicationRepository.countByMentorApplicationStatus(MentorApplicationStatus.REJECTED);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하나의 메서드 안에서 세 개의 쿼리를 실행하는데, 중간에 데이터가 변경되면 의도와 다른 개수가 나올 것 같습니다. 그룹바이로 하나의 쿼리로 될 거 같긴 합니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇겠네요, 도중에 상태가 변경된다면 값의 일관성이 깨질수도 있으니 jpql로 한번에 조회하는 식으로 수정하겠습니다!

Copy link
Contributor Author

@sukangpunch sukangpunch Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jpql 방식으로 조회를 시도 하면 Object[] 혹은, dto 리스트, queryDsl 을 활용하여 map 으로 받는 방식들이 존재하는데, 셋 다 비용이 있어 보여서 고민이 됩니다...
그리고 @transactional(readOnly = true) + mysql innoDB 의 REPEATABLE_READ 격리 수준 에선, 해당 트랜잭션 시작 시 스냅샷을 생성하고 트랜잭션 내의 쿼리들은 동일한 스냅샷을 읽는다고 합니다!
그래서 우려되는 문제는 발생하지 않지 않을까? 싶어서 답변 다시 드립니다!
@whqtker

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현 격리 수준에서 동일한 스냅샷을 읽기 때문에 정합성 깨짐 문제는 발생하지 않음 + 여러모로 JPQL을 통한 하나의 쿼리보다 현제 쿼리가 효율적인

말씀해주신 내용 전부 동의합니다 ~! 그대로 유지해도 될 거 같습니다.

UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER(HttpStatus.BAD_REQUEST.value(), "기타 학교를 선택한 경우 학교 정보를 입력할 수 없습니다."),
INVALID_UNIVERSITY_SELECT_TYPE(HttpStatus.BAD_REQUEST.value(), "지원하지 않는 학교 선택 방식입니다."),
MENTOR_ALREADY_EXISTS(HttpStatus.BAD_REQUEST.value(), "이미 존재하는 멘토입니다."),
MENTOR_APPLICATION_ALREADY_CONFIRM(HttpStatus.BAD_REQUEST.value(), "이미 승인 또는 거절된 멘토 승격 요청 입니다."),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소한 거긴 한데, CONFIRMED 가 더 나을 것 같습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하겠습니다!

* refactor: MENTOR_APPLICATION_ALREADY_CONFIRM -> MENTOR_APPLICATION_ALREADY_CONFIRMED 로 수정

* refactor: 멘토 지원서 거절 사유 관련하여 기획에 명시되지 않은 길이 제한 제거
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (1)

224-285: 잘 구성된 승인 테스트입니다!

  1. 테스트 커버리지

    • 대기중 승인, 대학 미선택, 이미 승인됨, 이미 거절됨, 존재하지 않음 - 모든 시나리오가 포함되었습니다.
  2. 상태 및 타임스탬프 검증 (Line 237-238)

    • 상태 전환과 approvedAt 설정 여부를 함께 검증하는 것이 좋습니다.
  3. 선택적 개선 제안 (Line 236)

    • .get() 대신 .orElseThrow()를 사용하면 테스트 실패 시 더 명확한 메시지를 얻을 수 있습니다.
-            MentorApplication result = mentorApplicationRepository.findById(mentorApplication2.getId()).get();
+            MentorApplication result = mentorApplicationRepository.findById(mentorApplication2.getId())
+                    .orElseThrow(() -> new AssertionError("승인된 멘토 지원서를 찾을 수 없습니다."));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7bf005 and 961b005.

📒 Files selected for processing (4)
  • src/main/java/com/example/solidconnection/admin/dto/MentorApplicationRejectRequest.java (1 hunks)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java (2 hunks)
  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/example/solidconnection/admin/dto/MentorApplicationRejectRequest.java
  • src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.462Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.
📚 Learning: 2025-11-17T06:30:49.502Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.

Applied to files:

  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
📚 Learning: 2025-11-20T14:03:56.462Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.462Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.

Applied to files:

  • src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1)

132-133: LGTM! 에러 코드가 잘 추가되었습니다.

  1. 네이밍 일관성

    • 기존 MENTORING_ALREADY_CONFIRMED(Line 125)와 동일한 패턴을 따르고 있습니다.
  2. HTTP 상태 코드

    • 검증 실패에 대해 BAD_REQUEST를 사용하는 것이 적절합니다.
  3. 메시지 명확성

    • 에러 메시지가 사용자에게 문제 상황을 명확하게 전달합니다.
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (3)

1-53: LGTM! Import 및 필드 주입이 적절하게 구성되었습니다.

  1. 정적 import

    • 에러 코드와 AssertJ 헬퍼 메서드가 명확하게 import 되었습니다.
  2. Repository 주입

    • 테스트에서 상태 전환 검증을 위해 MentorApplicationRepository를 주입한 것은 좋은 접근입니다.

287-340: 거절 테스트도 잘 구성되었습니다!

  1. 거절 사유 검증 (Line 302)

    • 거절 사유가 정확히 저장되는지 검증하는 것이 좋습니다.
  2. 예외 케이스 커버리지

    • 이미 승인/거절된 지원서에 대한 거절 시도, 존재하지 않는 지원서 거절 시도 모두 포함되어 있습니다.
  3. 동일한 선택적 개선 (Line 300)

    • 승인 테스트와 마찬가지로 .orElseThrow() 사용을 고려해 볼 수 있습니다.

342-374: 상태별 개수 조회 테스트가 깔끔합니다!

  1. 정상 케이스 검증 (Lines 346-359)

    • 각 상태별 개수가 정확히 일치하는지 검증합니다.
  2. 빈 데이터 케이스 (Lines 361-373)

    • deleteAll() 후 모든 상태의 개수가 0인지 확인하는 것은 경계 조건 테스트로 적절합니다.
  3. 트랜잭션 격리

    • @TestContainerSpringBootTest 환경에서 트랜잭션 롤백이 테스트 격리를 보장하므로 deleteAll()이 다른 테스트에 영향을 주지 않습니다.

@sukangpunch sukangpunch requested a review from whqtker November 28, 2025 08:15
Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경사항 확인했습니다 !
어드민 기능 담당하신 분 리뷰까지 받고 머지하는 게 좋을 것 같습니다. 고생하셨습니다 !

Copy link
Contributor

@JAEHEE25 JAEHEE25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다👍

Comment on lines 4 to 6
long approved,
long pending,
long rejected
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일관성을 위해서 approvedCount, pendingCount, rejectedCount로 수정하면 좋을 것 같습니다!

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공채때문에 너무 바빠서 죄송했습니다.. 코드를 아주 깔끔하게 잘 작성해주시는군요!
요즘 업무처내느라 이런 깔끔한 코드를 못봤는데 반성하게되네요
간단한 코멘트하나 남겼습니다! 고생하셨습니다~

Comment on lines 356 to 359
assertThat(response.approved()).isEqualTo(expectedApprovedCount.size());
assertThat(response.pending()).isEqualTo(expectedPendingCount.size());
assertThat(response.rejected()).isEqualTo(expectedRejectedCount.size());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 것들은 assertAll로 묶어주시면 감사하겠습니다!

Comment on lines 370 to 372
assertThat(response.approved()).isEqualTo(0L);
assertThat(response.pending()).isEqualTo(0L);
assertThat(response.rejected()).isEqualTo(0L);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도요! 그래야 ci 실패했을 때 디버깅하기 편해지더라구요

- refactor: 변수명, 필드명 일관성 맞추기

- test: assertAll 적용
@sukangpunch sukangpunch force-pushed the feat/575-admin-mentor-application-approval branch from d239f5b to c45a67c Compare December 19, 2025 06:40
@sukangpunch sukangpunch merged commit 3c342a8 into solid-connection:develop Dec 19, 2025
2 checks passed
whqtker added a commit that referenced this pull request Jan 19, 2026
* fix: yml 들여쓰기 수정 (#555)

* fix: yml 들여쓰기 수정

* fix: jdk 변경

* refactor: 멘토 및 채팅 관련 API 응답 수정 (#537)

* refactor: 멘토의 멘토링 조회 응답에서 mentoringId가 아니라 roomId를 포함하도록

* refactor: 파트너가 멘토인 경우 partnerId는 mentorId로

- AS IS: 멘토/멘티 모두 partnerId가 siteUserId
- TO BE: 멘티: siteUserId, 멘토: mentorId

* refactor: 응답의 senderId가 mentorId/siteUserId가 되도록

* refactor: senderId에 해당하는 chatParticipant가 없을 경우 예외 처리하는 로직 추가

* refactor: 메서드명에 맞게 시그니처 변경

* refactor: getChatMessages 메서드에서 응답으로 siteUserId를 넘겨주도록

- AS IS: mentorId(mentor) / siteUserId(mentee)
- TO BE: siteUserId(all)

* refactor: 헬퍼 메서드로 메서드 복잡성을 분산한다

* refactor: getChatPartner 메서드의 응답으로 siteUserId를 넘겨주도록

- AS IS: mentorId(mentor) / siteUserId(mentee)
- TO BE: siteUserId(all)

* refactor: CD 성능 개선 (#552)

* fix: deprecated된 base image를 eclipse-temurin:17-jdk로 변경

* refactor: scp 파일 전송하는 방식에서 GHCR로 push/pull하도록 변경

* fix: GHCR image 제거시 Org의 GITHUB_TOKEN 사용하도록 변경

* refactor : scp 파일 전송하는 방식에서 GHCR로 push/pull하도록 prod-cd.yml과 docker-compose.prod.yml 변경

* fix: prod 인스턴스 old image 이름 통일

* fix: prod-cd.yml StrictHostKeyChecking 옵션 문법 오류 수정

* fix: prod-cd.yml StrictHostKeyChecking 옵션 문법 오류 수정

* fix: dev-cd.yml Old images 정리 작업 중 이미지 이름 불일치 문제 해결

* chore: 마지막 줄 개행 추가

* chore: 마지막 줄 개행 추가

* feat: stage 인스턴스에 대한 최신 이미지 5개 유지 기능 및 old 이미지 제거 기능 추가

* chore: 중복된 환경변수 지정 제거

* chore: 중복된 pem키 생성 로직 제거

* fix: 잘못된 pem키 이름 수정

* refactor: 원격 호스트에서 pull할 경우, 최소 권한으로 실행하도록 Github App으로 임시토큰 발급하도록 수정

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 (#565)

* fix: GitHub app token permission 문제 해결 (#566)

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가

* fix: GitHub app token permission 문제 오류 해결 (#567)

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* fix: GitHub app token permission이 repo 레벨에서 부여되는 문제 해결 (#568)

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* fix: GitHub app token permission 권한 오류 해결 (#569)

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* test: fork repo의 작업 branch에서 해당 workflows가 실행되도록 임시 수정

* refactor: test용 설정 제거

* feat: claude.md 파일 추가 (#560)

* fix : 동일 멘토 멘티 중복 신청 불가능하도록 수정 (#563)

* fix : 동일 멘토 멘티 중복 신청 불가능하도록 수정
- UK 제약조건 추가
- flyway script 추가
- CustomException 추가
- Service 로직 수정
- Test code 추가

* fix : column명 오류 수정
- column명 camelCase -> snake_case로 변경

* fix : column명 오류 수정
- column명 name으로 명시

* fix: GitHub app token permission 권한 오류 해결 (#570)

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정

* fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가

* test: fork repo의 작업 branch에서 해당 workflows가 실행되도록 임시 수정

* refactor: test용 설정 제거

* fix: docker login username 불일치 문제

* refactor: 최소권한 원칙 적용을 위한 Action Job 분리

* refactor: 필요없는 주석 제거

* fix: GHCR 정리 권한 PAT로 해결 (#573)

* feat: 지원서가 APPROVED 인 유저의 멘토 생성 기능 추가 (#562)

* feat: 지원서가 APPROVED 인 유저의 멘토 생성 기능 추가

* refactor: submitMentorApplication 메서드의 멘토 지원 유효 검증 부분을 메서드로 추출

* refactor: MentorMyPage 생성, 수정 부분의 channel 생성, 업데이트 부분 중복 제거

* test: Mentor 생성 관련 테스트 추가

* fix: 코드래빗 리뷰 적용

* refactor: 멘토 생성 시 channelRequest가 null 일 떄 예외 처리

* feat: MentorApplicationRequest 필드에 유효성 어노테이션 추가

* test: 채널 검색 시 siteUserId로 조회하는 문제 해결

* fix: 리뷰 수정사항 적용

* fix: 파일 끝에 개행 추가

* refactor: 멘토 생성 메서드에서 siteUser의 검증 제외

* refactor: dto 단에서 채널 리스트 null 검증

* feat: MentorApplication에 termId 추가 flyway 스크립트 추가

* fix: flyway 버전 충돌 해결

* feat: 어드민 멘토 승격 요청 페이징 조회 기능 추가 (#576)

* feat: 어드민 멘토 지원서 페이징 조회 기능 추가

* feat: mentor/repository 패키지에 custom 패키지 추가

- custom 패키지에 페이징 조회를 책임지는 MentorApplicationFilterRepository 추가

- MentorApplicationSearchCondition 에서 넘긴 keyword 기반으로 닉네임, 권역, 나라, 학교명으로 필터링 검색 기능 추가

- MentorApplicationSearchCondition 에서 넘긴 mentorApplicationStatus 기반으로 승인, 거절, 진행중 으로 필터링 기능 추가

* test: 어드민 멘토 지원서 페이징 조회 테스트 추가

* feat: MentorApplication 엔티티에 approved_at 필드 추가 flyway 스크립트 작성

* fix: 파일 끝에 개행 추가

* refactor: 페이징 조회 시 count 쿼리에 불필요한 조인 막기

* fix: 코드래빗 리뷰 적용

* fix: flyway V39 스크립트 파일명 수정

* test: 테스트 코드 오류 수정, 검증 추가  

* test: 기대하는 값이랑 다른 테스트 응답을 수정합니다

* feat: 어드민 멘토 승격 지원서 승인/거절 기능, 상태 별 지원서 개수 조회 기능 추가 (#577)

* feat: 어드민 멘토 승격 지원서 승인/거절 기능 추가

* test: 어드민 멘토 지원서 승인/거절 테스트 추가

* feat: 멘토 지원서 상태별 개수 조회 기능 추가

* test: 멘토 지원서 상태별 개수 조회 테스트 추가

* fix: 대학이 선택되지 않은 멘토 지원서 승인 시 예외 발생하도록 수정

* refactor: 리뷰 내용 반영

* refactor: MENTOR_APPLICATION_ALREADY_CONFIRM -> MENTOR_APPLICATION_ALREADY_CONFIRMED 로 수정

* refactor: 멘토 지원서 거절 사유 관련하여 기획에 명시되지 않은 길이 제한 제거

* refactor: 리뷰 적용

* refactor: 변수명, 필드명 일관성 맞추기

* test: assertAll 적용

* feat: region 관련 관리 기능 추가 (#561)

* feat: 지역 생성 기능 구현 (AdminRegion)

지역을 생성하는 기능을 구현했습니다:
- AdminRegionCreateRequest: 지역 생성 요청 DTO
- AdminRegionResponse: 지역 응답 DTO
- AdminRegionService.createRegion(): 중복 검사를 포함한 지역 생성 로직
- AdminRegionController.createRegion(): HTTP POST 엔드포인트

중복 검사:
- 지역 코드 중복 확인
- 한글명 중복 확인

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* feat: 지역 수정/삭제/조회 기능 구현 및 테스트 추가 (AdminRegion)

지역 관리 기능을 완성했습니다:

구현 기능:
- AdminRegionUpdateRequest: 지역 수정 요청 DTO
- AdminRegionService.updateRegion(): 한글명 중복 검사를 포함한 지역 수정
- AdminRegionService.deleteRegion(): 지역 삭제
- AdminRegionService.getAllRegions(): 전체 지역 조회
- AdminRegionController: 수정/삭제/조회 HTTP 엔드포인트

테스트 코드 (AdminRegionServiceTest):
- CREATE: 정상 생성, 코드 중복, 한글명 중복 테스트
- UPDATE: 정상 수정, NOT_FOUND, 중복 한글명, 동일 한글명 테스트
- DELETE: 정상 삭제, NOT_FOUND 테스트
- READ: 빈 목록, 전체 조회 테스트

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* fix: 지역 관리 관련 에러코드 추가 (ErrorCode)

지역 관리 기능에 필요한 에러코드를 추가했습니다:
- REGION_NOT_FOUND: 존재하지 않는 지역
- REGION_ALREADY_EXISTS: 이미 존재하는 지역

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

* fix: jpa 오류 수정

* refactor: 코드리뷰 반영

---------

Co-authored-by: Claude <[email protected]>

* fix: config.alloy 파일 경로 불일치 문제 해결 (#586)

* fix: config.alloy 파일 경로 불일치 문제 해결

* refactor: docker-compose를 수정하는게 아닌 cd.yml의 경로를 수정하여 해결

* feat: 소셜 로그인 사용자는 비밀번호 변경을 할 수 없다. (#582)

* feat: 소셜 로그인 사용자는 비밀번호 변경을 할 수 없도록

* test: 소셜 로그인 사용자 비밀번호 변경 관련 테스트 코드 작성

* chore: 컨벤션에 맞게 메서드명 변경

- ~~ 예외가 발생한다

* chore: 충돌 해결

* fix: Upgrade 헤더 유무에 따라 Connection 헤더의 값을 동적으로 설정하도록 (#581)

* fix: Upgrade 헤더 유무에 따라 Connection 헤더의 값을 동적으로 설정하도록

- Upgrade 헤더가 존재하면(e.g. WebSocket) upgrade로 설정
- Upgrade 헤더가 존재하지 않으면 keep-alive로 설정

* chore: 서브모듈 업데이트

* feat: 멘토 지원서 대학교 매핑 기능, 대학 선택 상태 페이징 조건 추가 (#583)

* feat: 멘토 지원서 검색 조건에 UniversitySelectType 추가

* feat: 어드민 멘토 지원서 페이징 조회 응답에 UniversitySelectType 추가

* test: 멘토 지원서 조회 테스트 추가

- test: UniversitySelectType 기반 페이징 조회 테스트 추가

* feat: 멘토 지원서에 대학 매핑 기능 추가

* test: 멘토 지원서 대학 매핑 테스트 추가

* refactor: 의미 없는 import 제거

* refactor: 리뷰 내용 반영

* refactor: 개행 및 공백 추가

* refactor: pathVariable 네이밍을 kebab-case 로 통일

* refactor: Service 레이어의 검증 로직을 도메인으로 이동

* refactor: PENDING 상태 및 OTHER 타입 검증을 도메인 메서드로 관리

* refactor: assignUniversity() 호출 전 검증 책임을 도메인 엔티티에 위임

* test : assertAll 로 검증 그룹화

* refactor: 스프링 부트 앱 외의 사이드 인프라 배포 과정을 분리 (#592)

* refactor: dev 환경에서의 side-infra 배포 과정 분리

* refactor: prod 환경에서의 side-infra 배포 과정 분리

* refactor: docker-compose 가 실행되고 있지 않아도 스크립트가 실패하지 않게 변경

* fix: docker compose up 시에 사용할 환경변수 중 누락된 변수를 추가

* fix: S3 이름 불일치 문제 해결 (#594)

* fix: s3 이름 불일치 문제 해결

* fix: s3와의 연동된 cloudfront URL로 수정

* refactor: 분리한 사이드 인프라에 대해서 필요없는 파일 정리 (#596)

* test: flyway 스크립트를 검증하는 테스트 코드 작성 (#588)

* test: flyway 스크립트를 검증하는 테스트 코드 작성

* fix: DirtiesContext 어노테이션을 통해 기존 컨텍스트를 폐기하도록

- 새로운 MySQL 환경에서 마이그레이션이 이루어지도록 수정

* fix: flyway 검증용의 별도의 MySQL 컨테이너를 사용하도록

* chore: 테스트 의도를 쉽게 이해할 수 있도록 주석 추가

* chore: 명시적으로 컨테이너를 시작하도록

- 또한 MySQLTestContainer 코드와 유사한 컨벤션을 가지도록 수정

* refactor: 게시글 조회 응답에 댓글 deprecated 여부 포함하도록 (#599)

* feat: 유저의 멘토 지원서 신청 이력 조회 기능 추가 (#603)

* feat: 유저의 멘토 지원 이력 조회 기능 추가

* refactor: 매개변수 타입 통일

* refactor: long 타입을 Long 으로 수정

* test: 멘토 지원서 이력 조회 테스트 추가

* test: MentorApplicationFixtureBuilder 에 rejectedReason 필드 및 빌더 메서드 추가

* refactor: 리뷰 사항 적용

* test: 멘토 지원서 이력 조회 에서 user와 university 재사용

* refactor: 긴 uri 를 짧게 수정

* refactor: 서브모듈 해시값 되돌리기

* refactor: 개행 지우기

* refactor: applicationOrder 자료형을 long 으로 수정

* fix: applicationOrder 를 int 자료형으로 처리하도록 복구

- 순서를 나타내고, 해당 값이 21억을 넘길 수 없다 판단하여 더 적합한 int 자료형으로 복구

* test: long type 을 기대하던 테스트 에러 해결

* fix: 탈퇴한 사용자가 물리적 삭제가 되지 않았던 문제를 해결한다 (#574)

* refactor: FK에 ON DELETE CASCADE 옵션 추가

* refactor: 삭제 메서드로 사용자 연관 데이터를 삭제하도록

* feat: 어드민 유저 차단 기능 추가 (#604)

* feat: 어드민 차단 기능

* test: 어드민 차단 기능

* feat: API 성능 로깅, 쿼리 별 메트릭 전송 추가 (#602)

* feat: HTTP 요청/응답 로깅 필터 구현

- traceId 기반 요청 추적
- 요청/응답 로깅
- CustomExceptionHandler와 중복 로깅 방지
- Actuator 엔드포인트 로깅 제외

* feat: ExceptionHandler에 중복 로깅 방지 플래그 및 userId 로깅 추가

* feat: API 수행시간 로깅 인터셉터 추가

* feat: ApiPerf 인터셉터, Logging 필터 빈 등록

* refactor: logback 설정 변경

- info, warn, error, api_perf 로 로그 파일 분리해서 관리

* feat: 쿼리 별 수행시간 메트릭 모니터링 추가

* feat: 데이터소스 프록시 의존성 및 config 파일 추가

* feat: 데이터 소스 프록시가 metric을 찍을 수 있도록 listener 클래스 추가

* feat: 요청 시 method, uri 정보를 listener에서 활용하기 위해 RequestContext 및 관련 interceptor 추가

* refactor: 비효율적인 Time 빌더 생성 개선

- Time.builder 를 사용하면 매번 빌더를 생성하여 비효율적인 문제를 meterRegistry.timer 방식으로 해결

* feat: 로깅을 위해 HttpServeletRequest 속성에 userId 추가

* refactor: logback 설정 중 local은 console만 찍도록 수정

* refactor: FILE_PATTERN -> LOG_PATTERN 으로 수정

* test: TokenAuthenticationFilter에서 request에 userId 설정 검증 추가

- principal 조회 예외를 막기 위해 siteUserDetailsService given 추가

* refacotr: 코드 래빗 리뷰사항 반영

* test: 중복되는 테스트 제거

* refactor: 사용하지 않는 필드 제거

* refactor: 리뷰 내용 반영

* refactor: ApiPerformanceInterceptor에서 uri 정규화 관련 코드 제거

* refactor: ApiPerformanceInterceptor에서 if-return 문을 if-else 문으로 수정

* refactor: 추가한 interceptor 의 설정에 actuator 경로 무시하도록 셋팅

* refactor: 중복되는 의존성 제거

* refactor: 로깅 시 민감한 쿼리 파라미터 마스킹

- EXCLUDE_QUERIES 에 해당하는 쿼리 파라미터 KEY 값의 VALUE 를 masking 값으로 치환

* refactor: 예외 처리 후에도 Response 로그 찍도록 수정

* refactor: CustomExceptionHandler 원상복구

- Response 로그를 통해 user를 추적할 수 있으므로 로그에 userId 를 추가하지 않습니다

* refactor: 리뷰 사항 반영

* refactor: RequestContext 빌더 제거

* refactor: RequestContextInterceptor import 수정

* refactor: logback yml 파일에서 timestamp 서버 시간과 동일한 규격으로 수정

* refactor: ApiPerformanceInterceptor 에서 동일 내용 로그 중복으로 찍는 문제 수정

* fix: decode를 두 번 하는 문제 수정

* test: 로깅 관련 filter, interceptor 테스트 추가

* refactor: 코드래빗 리뷰사항 반영

* test: contains 로 비교하던 검증 로직을 isEqualTo 로 수정

* test: preHandle 테스트 에서 result 값을 항상 검증

* refactor: 단위테스트에 TestContainer 어노테이션 제거

* fix: conflict 해결

* fix: docker-compose 충돌 해결 (#610)

* chore: release github action 임의 실행 추가

* refactor: 기본 추천 대학 후보 추가 (#161)

* fix: config.alloy 경로 수정

* hotfix: 모의지원 현황 어드민 권한 제거

* hotfix: import 제거

* chore: 서브모듈 해시 업데이트 (#611)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 어드민 멘토 승격 지원서 승인/거절 기능 추가 및 상태 별 지원서 개수 조회 기능 추가

4 participants